package edu.washington.ling.syntric.proc;

import java.util.ArrayList;
import java.util.Set;

import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;

import edu.washington.ling.syntric.io.*;
import edu.washington.ling.syntric.struct.error.*;
import edu.washington.ling.syntric.struct.tree.Node;
import edu.washington.ling.syntric.struct.tree.Tree;
import edu.washington.ling.syntric.util.*;

public class CheckConsistency {
	private static CheckConsistencyOptions opts = new CheckConsistencyOptions();
	
	private static PrintingExceptionHandler pHandler = new PrintingExceptionHandler();
	

	private static ArrayList<Node> getInconsistentNodes(Node n){
		ArrayList<Node> badNodes = new ArrayList<Node>(); 
		Set<String> headWords = n.getHeadWords();
		if (headWords.size() != 1){
			badNodes.add(n);
		}
		for (Node child: n.getPSChildren()) {
			badNodes.addAll(getInconsistentNodes(child));
		}
		return badNodes; 
	}
	
	public static boolean checkForConsistency(Tree tree){
		TreeUtils.labelHeadWordsForTree(tree);
		boolean isConsistent = true;
		ArrayList<Node> badNodes = new ArrayList<Node>();
		badNodes.addAll(getInconsistentNodes(tree.getPSRoot()));
		if (badNodes.size() > 0){
			isConsistent = false;
		}
		return isConsistent;
	}
	
	private static void checkConsistencyOfInputFile(){
		int numExamples = 0;
		int numWithBoth = 0;
		int numConst = 0;
		int numInconstTokens = 0;
		int numInconstStructure = 0;
		
		try {
			ExampleDocReader exfr = ExampleDocReader.newReader(opts.inputFilePath, "xml");
			ExampleDoc exf = exfr.read();
			ExampleDocWriter exfw = ExampleDocWriter.newWriter(opts.outputFilePath, "xml");
			TreeReader dsr = TreeReader.newReader(opts.dsTreeFormat);
			TreeReader psr = TreeReader.newReader(opts.psTreeFormat);
			Example exr = null;
			while ((exr = exf.getNextExample()) != null) {
				numExamples++;
				if (exr.hasTreeRep("DS-Const") && exr.hasTreeRep("PS")) {
					numWithBoth++;
					TreeRepresentation dsrep = exr.getTreeRep("DS-Const");
					try {
						Tree ds = dsr.readTree(dsrep);
						Tree ps = psr.readTree(exr.getTreeRep("PS"));
						Tree tree = MergeUtils.mergeDSandPSTrees(ds, ps);
						if (checkForConsistency(tree)) {
							numConst++;
						} else {
							numInconstStructure++;
							dsrep.addMetaData("inconsistent");
							dsrep.addMetaData("inconstStructure");
							String errStr = String.format("Warning: Inconsistent structures in example '%s'\n", exr.getID());
							System.err.println(errStr);
						}
					}
					catch (TreeMergeException e) {
						numInconstTokens++;
						dsrep.addMetaData("inconsistent");
						dsrep.addMetaData("inconstTokens");
						String errStr = String.format("Could not merge representations for example '%s'", exr.getID());
						pHandler.handle(e, errStr);
					}
					catch (UnreadableRepresentationException e) {
						String errStr = String.format("Could not process representation for example '%s'", exr.getID());
						pHandler.handle(e, errStr);
					}
				}
				exfw.writeExample(exr);
			}
			exfw.close();
			System.out.println("Check consistency of DS-const and PS was successful.");
			System.out.println(String.format("%d/%d tree pairs were found to be consistent", numConst, numWithBoth));
			System.out.println(String.format("%d tree pairs were inconsistent due to unmatched empty categories", numInconstTokens));
			System.out.println(String.format("%d tree pairs were inconsistent due to structural differences", numInconstStructure));
			System.out.println(String.format("Processed %d total examples", numExamples));
		}
		catch(Exception e) {
			pHandler.handle(e, "Check consistency failed.", System.out);
			e.printStackTrace(System.out);
			System.exit(-1);
		}
	}
	
	public static void main(String[] args) {
		CmdLineParser parser = new CmdLineParser(opts);
		try {
			parser.parseArgument(args);
			checkConsistencyOfInputFile();
		}
		catch(CmdLineException e) {
			System.err.println(e.getMessage());
			System.err.print("Usage: CheckConsistency");
			parser.printSingleLineUsage(System.err);
			System.err.println();
			parser.printUsage(System.err);
			System.exit(1);
		}
	}
}
